﻿// Screenshotz
//
// Source from SBIP.Helper.BitmapConverter Library
// SBIP framework
//
// Copyright © Frank Nagl, 2008-2012
// admin@franknagl.de
// www.franknagl.de
//
namespace Screenshotz
{
    using System;
    using System.Drawing;
    using System.Drawing.Imaging;

    /// <summary>
    /// Source from SBIP.Helper.BitmapConverter: 
    /// Provides several static functions to convert image file(s) in a 
    /// special image format.
    /// </summary>
    public static class ImageConverter
    {
        #region From SBIP
        /// <summary>
        /// Converts 32 bpp bitmap to 8 bpp bitmap by mean value of all 3 color channels.
        /// </summary>
        /// <param name="source">The bitmap, which will be converted.</param>
        /// <returns>Result (8 bpp) bitmap.</returns>
        public static Bitmap ARGBTo8Bit(Bitmap source)
        {
            // check image format
            if (source.PixelFormat != PixelFormat.Format32bppRgb &&
                source.PixelFormat != PixelFormat.Format32bppArgb)
                throw new ArgumentException("Source image can be color (32 bpp) image only");

            int w = source.Width;
            int h = source.Height;
            Rectangle rect = new Rectangle(0, 0, w, h);

            Bitmap destination = new Bitmap(w, h, PixelFormat.Format8bppIndexed);
            BitmapData dstData = destination.LockBits
                (rect, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);

            // lock source bitmap data
            BitmapData srcData = source.LockBits(
                rect, ImageLockMode.ReadOnly, source.PixelFormat);
            var dstOffset = dstData.Stride - w;

            // process image
            unsafe
            {
                byte* src = (byte*)srcData.Scan0.ToPointer();
                byte* dst = (byte*)dstData.Scan0.ToPointer();
                // for each line
                for (int y = 0; y < h; y++)
                {
                    // for each pixel in line
                    for (int x = 0; x < w; x++, src += 4, dst++)
                    {
                        int avg = src[RGBA.R] + src[RGBA.G] + src[RGBA.B];
                        avg /= 3;
                        *dst = (byte)avg;
                    }
                    dst += dstOffset;
                }
            }
            // unlock destination image
            source.UnlockBits(srcData);
            destination.UnlockBits(dstData);
            SetColorPaletteToGray(destination);

            return destination;
        }

        /// <summary>
        /// Clones the bitmap with the correct pixel format.
        /// </summary>
        /// <param name="source">Source image.</param>
        /// <param name="width">The new width of the result image.</param>
        /// <param name="height">The new height of the result image.</param>
        /// <param name="format">Pixel format of the result image.</param>
        /// <returns>
        /// Returns clone of the source image with specified pixel format.
        /// </returns>
        /// <remarks>
        /// The original 
        /// <see cref="System.Drawing.Bitmap.Clone(System.Drawing.Rectangle, System.Drawing.Imaging.PixelFormat)">
        /// Bitmap.Clone()</see>
        /// does not produce the desired result - it does not create a clone with specified pixel format.
        /// More of it, the original method does not create an actual clone - it does not create a copy
        /// of the image.
        /// </remarks>
        public static Bitmap CloneBitmap(
            Bitmap source,
            int width,
            int height,
            PixelFormat format)
        {
            if (!IsColorImage(source.PixelFormat))
            {
                throw new ArgumentException(
                    "Source image can be color (24 or 32 bpp) image only");
            }
            // create new image with desired pixel format
            Bitmap bitmap = new Bitmap(width, height, format);

            // draw source image on the new one using Graphics
            Graphics g = Graphics.FromImage(bitmap);
            g.DrawImage(source, 0, 0, width, height);
            g.Dispose();

            return bitmap;
        }

        /// <summary>
        /// Determines whether the specified format represents a color image.
        /// </summary>
        /// <param name="format">The specified image format.</param>
        /// <returns>
        ///   <c>true</c> if the specified format represents a color image; 
        /// otherwise, <c>false</c>.
        /// </returns>
        public static bool IsColorImage(PixelFormat format)
        {
            // check image format
            if (format != PixelFormat.Format24bppRgb &&
                format != PixelFormat.Format32bppArgb &&
                format != PixelFormat.Format32bppRgb &&
                format != PixelFormat.Format32bppPArgb)
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// Converts 24 bpp bitmap to 8 bpp bitmap by mean value of all 3 channels.
        /// </summary>
        /// <param name="source">The bitmap, which will be converted.</param>
        /// <returns>Result (8 bpp) bitmap.</returns>
        public static Bitmap RGBTo8Bit(Bitmap source)
        {
            // check image format
            if (source.PixelFormat != PixelFormat.Format24bppRgb)
                throw new ArgumentException("Source image can be color (24 bpp) image only");

            int w = source.Width;
            int h = source.Height;
            Rectangle rect = new Rectangle(0, 0, w, h);

            Bitmap destination = new Bitmap(w, h, PixelFormat.Format8bppIndexed);
            BitmapData dstData = destination.LockBits
                (rect, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);

            // lock source bitmap data
            BitmapData srcData = source.LockBits(
                rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            var srcOffset = srcData.Stride - w * 3;
            var dstOffset = dstData.Stride - w;

            // process image
            unsafe
            {
                byte* src = (byte*)srcData.Scan0.ToPointer();
                byte* dst = (byte*)dstData.Scan0.ToPointer();
                // for each line
                for (int y = 0; y < h; y++)
                {
                    // for each pixel in line
                    for (int x = 0; x < w; x++, src += 3, dst++)
                    {
                        int avg = src[RGBA.R] + src[RGBA.G] + src[RGBA.B];
                        avg /= 3;
                        *dst = (byte)avg;
                    }
                    src += srcOffset;
                    dst += dstOffset;
                }
            }
            // unlock destination image
            source.UnlockBits(srcData);
            destination.UnlockBits(dstData);
            SetColorPaletteToGray(destination);

            return destination;
        }

        /// <summary>
        /// Sets the color palette of the <paramref name="bitmap"/> to gray.
        /// </summary>
        /// <param name="bitmap">The bitmap to set its color palette to gray.</param>
        public static void SetColorPaletteToGray(Bitmap bitmap)
        {
            // get palette
            ColorPalette cp = bitmap.Palette;
            // init palette
            for (int i = 0; i < 256; i++)
            {
                cp.Entries[i] = Color.FromArgb(i, i, i);
            }
            // set palette back
            bitmap.Palette = cp;
        }

        /// <summary>
        /// Converts 32 bpp bitmap to 24 bpp bitmap.
        /// </summary>
        /// <param name="source">The bitmap, which will be converted.</param>
        /// <returns>Result (24 bpp) bitmap.</returns>
        // ReSharper disable InconsistentNaming
        public static Bitmap ARGBToRGB(Bitmap source)
        // ReSharper restore InconsistentNaming
        {
            int w = source.Width;
            int h = source.Height;
            Rectangle rect = new Rectangle(0, 0, w, h);

            Bitmap destination = new Bitmap(w, h, PixelFormat.Format24bppRgb);
            BitmapData dstData = destination.LockBits
                (rect, ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);

            // lock source bitmap data
            BitmapData srcData = source.LockBits(
                rect, ImageLockMode.ReadOnly, source.PixelFormat);

            var dstOffset = dstData.Stride - w * 3;

            // process image
            unsafe
            {
                byte* src = (byte*)srcData.Scan0.ToPointer();
                byte* dst = (byte*)dstData.Scan0.ToPointer();
                // for each line
                for (int y = 0; y < h; y++)
                {
                    // for each pixel in line
                    for (int x = 0; x < w; x++, src += 4, dst += 3)
                    {
                        dst[RGBA.R] = src[RGBA.R];
                        dst[RGBA.G] = src[RGBA.G];
                        dst[RGBA.B] = src[RGBA.B];
                    }
                    dst += dstOffset;
                }
            }
            // unlock destination image
            source.UnlockBits(srcData);
            destination.UnlockBits(dstData);

            return destination;
        }
        #endregion From SBIP
    }
}
